// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Copyright 2022 The Lynx Authors. All rights reserved.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.

#ifndef CLAY_EXAMPLE_GLFW_CUSTOM_TASK_RUNNER_H_
#define CLAY_EXAMPLE_GLFW_CUSTOM_TASK_RUNNER_H_

#include <mutex>
#include <unordered_map>
#include <utility>

#include "base/include/fml/macros.h"
#include "base/include/fml/task_runner.h"

namespace clay {
namespace example {

class CustomTaskRunner final : public fml::TaskRunner {
 public:
  //----------------------------------------------------------------------------
  /// @brief      A
  ///
  struct DispatchTable {
    //--------------------------------------------------------------------------
    /// Delegates responsibility of deferred task execution to the embedder.
    /// Once the embedder gets the task, it must call
    /// `CustomTaskRunner::PostTask` with the supplied `task_baton` on the
    /// correct thread after the tasks `target_time` point expires.
    ///
    std::function<void(CustomTaskRunner* task_runner, uint64_t task_baton,
                       fml::TimePoint target_time)>
        post_task_callback;
    //--------------------------------------------------------------------------
    /// Asks the embedder if tasks posted to it on this task task runner via the
    /// `post_task_callback` will be executed (after task expiry) on the calling
    /// thread.
    ///
    std::function<bool(void)> runs_task_on_current_thread_callback;
  };

  //----------------------------------------------------------------------------
  /// @brief      Create a task runner with a dispatch table for delegation of
  ///             task runner responsibility to the embedder. When embedders
  ///             specify task runner dispatch tables that service tasks on the
  ///             same thread, they also must ensure that their
  ///             `embedder_idetifier`s match. This allows the engine to
  ///             determine task runner equality without actually posting tasks
  ///             to the task runner.
  ///
  /// @param[in]  table                The task runner dispatch table.
  /// @param[in]  embedder_identifier  The embedder identifier
  ///
  CustomTaskRunner(DispatchTable table, size_t embedder_identifier);

  // |fml::TaskRunner|
  ~CustomTaskRunner() override;

  //----------------------------------------------------------------------------
  /// @brief      The unique identifier provided by the embedder for the task
  ///             runner. Embedders whose dispatch tables service tasks on the
  ///             same underlying OS thread must ensure that their identifiers
  ///             match. This allows the engine to determine task runner
  ///             equality without posting tasks on the thread.
  ///
  /// @return     The embedder identifier.
  ///
  size_t GetEmbedderIdentifier() const;

  bool PostTask(uint64_t baton);

  void OnThreadHostDestroyed();

  // |fml::TaskRunner|
  void PostTask(lynx::base::closure task) override;

 private:
  const size_t embedder_identifier_;
  DispatchTable dispatch_table_;
  std::recursive_mutex tasks_mutex_;
  uint64_t last_baton_ = 0;
  std::unordered_map<uint64_t, std::pair<fml::TimePoint, lynx::base::closure>>
      pending_tasks_;
  fml::TaskQueueId placeholder_id_;
  bool thread_host_destroyed_ = false;

  // |fml::TaskRunner|
  void PostTaskForTime(lynx::base::closure task,
                       fml::TimePoint target_time) override;

  // |fml::TaskRunner|
  void PostDelayedTask(lynx::base::closure task, fml::TimeDelta delay) override;

  // |fml::TaskRunner|
  bool RunsTasksOnCurrentThread() override;

  // |fml::TaskRunner|
  fml::TaskQueueId GetTaskQueueId() override;

  BASE_DISALLOW_COPY_AND_ASSIGN(CustomTaskRunner);
};

}  // namespace example
}  // namespace clay

#endif  // CLAY_EXAMPLE_GLFW_CUSTOM_TASK_RUNNER_H_
